package org.springframework.security.web;

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.log.LogMessage;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.context.SecurityContextHolderStrategy;
import org.springframework.security.web.firewall.*;
import org.springframework.security.web.util.ThrowableAnalyzer;
import org.springframework.security.web.util.UrlUtils;
import org.springframework.util.Assert;
import org.springframework.web.filter.GenericFilterBean;

import java.io.IOException;
import java.util.Collections;
import java.util.List;

/**
 * @author Dillon
 * @date 2024/6/26
 * @slogan 致敬大师 致敬未来的你
 * @desc security核心类，通过该类将security的所有过滤器集中到spring项目中的
 */
public class FilterChainProxy extends GenericFilterBean {

	private static final Log logger = LogFactory.getLog(FilterChainProxy.class);

	private static final String FILTER_APPLIED = FilterChainProxy.class.getName().concat(".APPLIED");

	private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder.getContextHolderStrategy();

	/**
	 * 过滤器链列表 每一个过滤器链里面记录着一组过滤器
	 */
	private List<SecurityFilterChain> filterChains;

	private FilterChainValidator filterChainValidator = new NullFilterChainValidator();

	private HttpFirewall firewall = new StrictHttpFirewall();

	/**
	 * 请求拒绝处理器
	 */
	private RequestRejectedHandler requestRejectedHandler = new HttpStatusRequestRejectedHandler();

	/**
	 * 异常分析器
	 */
	private final ThrowableAnalyzer throwableAnalyzer = new ThrowableAnalyzer();

	/**
	 * 虚拟过滤器链代理类
	 */
	private FilterChainDecorator filterChainDecorator = new VirtualFilterChainDecorator();

	public FilterChainProxy() {
	}

	/**
	 * 构造函数，用于初始化单个过滤器链
	 * @param chain 单个过滤器链
	 */
	public FilterChainProxy(SecurityFilterChain chain) {
		this(Collections.singletonList(chain));
	}

	/**
	 * 构造函数，用于初始化多个过滤器链，不同的过滤器链可以对不同的一类接口执行过滤
	 * @param filterChains 一组过滤器链
	 */
	public FilterChainProxy(List<SecurityFilterChain> filterChains) {
		this.filterChains = filterChains;
	}

	@Override
	public void afterPropertiesSet() {
		this.filterChainValidator.validate(this);
	}

	/**
	 * 核心方法，每次请求都会经过该过滤器链，执行特定的权限过滤
	 *
	 * @param request 请求封装类
	 * @param response 响应封装类
	 * @param chain 过滤器链
	 * @throws IOException 执行异常
	 * @throws ServletException 执行异常
	 */
	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		// 临界值判断，是否第一次经过该过滤器，如果不是，直接调用过滤器，而不做其他操作， 这是为了确保每一个请求都会被适当的过滤器处理
		boolean clearContext = request.getAttribute(FILTER_APPLIED) == null;
		if (!clearContext) {
			doFilterInternal(request, response, chain);
			return;
		}
		// 如果是第一次请求，则设置调用标记为，同时最后也会清理上下文和清空标记位
		try {
			request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
			doFilterInternal(request, response, chain);
		} catch (Exception ex) {
			// 获取抛出的异常链
			Throwable[] causeChain = this.throwableAnalyzer.determineCauseChain(ex);
			// 异常判断处理，如果不是请求拒绝异常，继续抛出
			Throwable requestRejectedException = this.throwableAnalyzer
					.getFirstThrowableOfType(RequestRejectedException.class, causeChain);
			if (!(requestRejectedException instanceof RequestRejectedException)) {
				throw ex;
			}
			// 如果是 请求拒绝异常，则调用请求拒绝处理器
			this.requestRejectedHandler.handle((HttpServletRequest) request, (HttpServletResponse) response,
					(RequestRejectedException) requestRejectedException);
		} finally {
			// 执行完成，清空上下文并移除标记
			this.securityContextHolderStrategy.clearContext();
			request.removeAttribute(FILTER_APPLIED);
		}
	}

	/**
	 * 过滤器链执行核心方法
	 * @param request 请求对象
	 * @param response 响应对象
	 * @param chain 原始过滤器链
	 * @throws IOException 执行异常
	 * @throws ServletException 执行异常
	 */
	private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		// 防火墙包装类获取
		FirewalledRequest firewallRequest = this.firewall.getFirewalledRequest((HttpServletRequest) request);
		HttpServletResponse firewallResponse = this.firewall.getFirewalledResponse((HttpServletResponse) response);
		// 获取与当前请求匹配的过滤器链
		List<Filter> filters = getFilters(firewallRequest);
		// 如果不匹配，日志记录，防火墙重置，执行完成
		if (filters == null || filters.isEmpty()) {
			if (logger.isTraceEnabled()) {
				logger.trace(LogMessage.of(() -> "No security for " + requestLine(firewallRequest)));
			}
			firewallRequest.reset();
			this.filterChainDecorator.decorate(chain).doFilter(firewallRequest, firewallResponse);
			return;
		}
		// 日志记录
		if (logger.isDebugEnabled()) {
			logger.debug(LogMessage.of(() -> "Securing " + requestLine(firewallRequest)));
		}
		// 回调，在过滤器链执行完成后，重置防火墙 执行真正原始过滤器
		FilterChain reset = (req, res) -> {
			if (logger.isDebugEnabled()) {
				logger.debug(LogMessage.of(() -> "Secured " + requestLine(firewallRequest)));
			}
			firewallRequest.reset();
			chain.doFilter(req, res);
		};
		this.filterChainDecorator.decorate(reset, filters).doFilter(firewallRequest, firewallResponse);
	}

	/**
	 * 获取与当前请求匹配的过滤器链
	 * @param request 请求对象
	 * @return 过滤器链
	 */
	private List<Filter> getFilters(HttpServletRequest request) {
		int count = 0;
		// 循环配置的过滤器链
		for (SecurityFilterChain chain : this.filterChains) {
			if (logger.isTraceEnabled()) {
				logger.trace(LogMessage.format("尝试匹配请求 %s (%d/%d)", chain, ++count, this.filterChains.size()));
			}
			// 如果请求匹配器可以匹配上，则返回过滤器链
			// 所以这里过滤器链的配置位置很重要
			if (chain.matches(request)) {
				return chain.getFilters();
			}
		}
		return null;
	}

	/**
	 * Convenience method, mainly for testing.
	 *
	 * @param url the URL
	 * @return matching filter list
	 */
	public List<Filter> getFilters(String url) {
		return getFilters(this.firewall.getFirewalledRequest(new FilterInvocation(url, "GET").getRequest()));
	}

	/**
	 * @return the list of {@code SecurityFilterChain}s which will be matched against and
	 * applied to incoming requests.
	 */
	public List<SecurityFilterChain> getFilterChains() {
		return Collections.unmodifiableList(this.filterChains);
	}

	/**
	 * Sets the {@link SecurityContextHolderStrategy} to use. The default action is to use
	 * the {@link SecurityContextHolderStrategy} stored in {@link SecurityContextHolder}.
	 *
	 * @since 5.8
	 */
	public void setSecurityContextHolderStrategy(SecurityContextHolderStrategy securityContextHolderStrategy) {
		Assert.notNull(securityContextHolderStrategy, "securityContextHolderStrategy cannot be null");
		this.securityContextHolderStrategy = securityContextHolderStrategy;
	}

	/**
	 * Used (internally) to specify a validation strategy for the filters in each
	 * configured chain.
	 *
	 * @param filterChainValidator the validator instance which will be invoked on during
	 *                             initialization to check the {@code FilterChainProxy} instance.
	 */
	public void setFilterChainValidator(FilterChainValidator filterChainValidator) {
		this.filterChainValidator = filterChainValidator;
	}

	/**
	 * Used to decorate the original {@link FilterChain} for each request
	 *
	 * <p>
	 * By default, this decorates the filter chain with a {@link VirtualFilterChain} that
	 * iterates through security filters and then delegates to the original chain
	 *
	 * @param filterChainDecorator the strategy for constructing the filter chain
	 * @since 6.0
	 */
	public void setFilterChainDecorator(FilterChainDecorator filterChainDecorator) {
		Assert.notNull(filterChainDecorator, "filterChainDecorator cannot be null");
		this.filterChainDecorator = filterChainDecorator;
	}

	/**
	 * Sets the "firewall" implementation which will be used to validate and wrap (or
	 * potentially reject) the incoming requests. The default implementation should be
	 * satisfactory for most requirements.
	 *
	 * @param firewall
	 */
	public void setFirewall(HttpFirewall firewall) {
		this.firewall = firewall;
	}

	/**
	 * Sets the {@link RequestRejectedHandler} to be used for requests rejected by the
	 * firewall.
	 *
	 * @param requestRejectedHandler the {@link RequestRejectedHandler}
	 * @since 5.2
	 */
	public void setRequestRejectedHandler(RequestRejectedHandler requestRejectedHandler) {
		Assert.notNull(requestRejectedHandler, "requestRejectedHandler may not be null");
		this.requestRejectedHandler = requestRejectedHandler;
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append("FilterChainProxy[");
		sb.append("Filter Chains: ");
		sb.append(this.filterChains);
		sb.append("]");
		return sb.toString();
	}

	private static String requestLine(HttpServletRequest request) {
		return request.getMethod() + " " + UrlUtils.buildRequestUrl(request);
	}

	/**
	 * 虚拟的过滤器链装饰实现类
	 * 主要作用循环加入的过滤器，执行完成后，执行原过滤器
	 */
	private static final class VirtualFilterChain implements FilterChain {

		/**
		 * 原过滤器链
		 */
		private final FilterChain originalChain;

		/**
		 * 需要执行的过滤器链
		 */
		private final List<Filter> additionalFilters;

		/**
		 * 需要执行的过滤器数量
		 */
		private final int size;

		/**
		 * 当前知道到过滤器位置
		 */
		private int currentPosition = 0;

		/**
		 * 构造函数
		 * @param chain 原过滤器链
		 * @param additionalFilters 需要执行的过滤器列表
		 */
		private VirtualFilterChain(FilterChain chain, List<Filter> additionalFilters) {
			this.originalChain = chain;
			this.additionalFilters = additionalFilters;
			this.size = additionalFilters.size();
		}

		@Override
		public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
			// 如果所有过滤器执行完毕，则执行原过滤器
			if (this.currentPosition == this.size) {
				this.originalChain.doFilter(request, response);
				return;
			}
			// 依次调用需要调用的过滤器
			this.currentPosition++;
			Filter nextFilter = this.additionalFilters.get(this.currentPosition - 1);
			if (logger.isTraceEnabled()) {
				String name = nextFilter.getClass().getSimpleName();
				logger.trace(LogMessage.format("Invoking %s (%d/%d)", name, this.currentPosition, this.size));
			}
			nextFilter.doFilter(request, response, this);
		}

	}

	public interface FilterChainValidator {

		void validate(FilterChainProxy filterChainProxy);

	}

	private static class NullFilterChainValidator implements FilterChainValidator {

		@Override
		public void validate(FilterChainProxy filterChainProxy) {
		}

	}

	/**
	 * A strategy for decorating the provided filter chain with one that accounts for the
	 * {@link SecurityFilterChain} for a given request.
	 *
	 * @author Josh Cummings
	 * @since 6.0
	 */
	/**
	 * 过滤器链装饰接口
	 */
	public interface FilterChainDecorator {

		/**
		 * 装饰 原过滤器
		 * @param original 原过滤器
		 * @return 装饰后的过滤器
		 */
		default FilterChain decorate(FilterChain original) {
			return decorate(original, Collections.emptyList());
		}

		/**
		 * 装饰 原过滤器
		 * @param original 原过滤器
		 * @param filters 需要加入的过滤器
		 * @return 装饰后的过滤器
		 */
		FilterChain decorate(FilterChain original, List<Filter> filters);

	}

	/**
	 * 虚拟的过滤器链装饰类实现
	 */
	public static final class VirtualFilterChainDecorator implements FilterChainDecorator {

		/**
		 * 构建装饰类
		 * @param original 原过滤器
		 * @param filters 需要加入的过滤器
		 * @return FilterChain 装饰类
		 */
		@Override
		public FilterChain decorate(FilterChain original, List<Filter> filters) {
			return new VirtualFilterChain(original, filters);
		}

	}

}
